package com.sirun.ble.utils.sdk;

import static com.sirun.ble.constants.BleConfig.BleSdkTag;
import static com.sirun.ble.constants.BleConfig.CMD_TYPE_DEFAULT;
import static com.sirun.ble.constants.BleConfig.CMD_TYPE_LOCK;
import static com.sirun.ble.constants.BleConfig.CMD_TYPE_UNLOCK;

import android.bluetooth.BluetoothGattCharacteristic;
import android.content.Context;

import com.sirun.ble.bean.CarStatusBean;
import com.sirun.ble.constants.BleConfig;
import com.sirun.ble.enums.VehicleStatusEnum;
import com.sirun.ble.listeners.OnCmdListener;
import com.sirun.ble.utils.AwByteUtil;
import com.sirun.ble.utils.AwLog;
import com.sirun.ble.utils.AwSystemUtil;
import com.sirun.ble.utils.sec.AwAesUtil;

/**
 *  @author : hzw
 */
public class AppBleCmdUtil {
    private static final String TAG = "BleSdkTag";
    private static final byte CMD_SERVER_AUTH = (byte) 0x81;
    private static final byte CMD_SERVER_CONTROL_LOCK = (byte) 0x81;
    private static final byte CMD_SERVER_CONTROL_FIND = (byte) 0x82;
    private static final byte CMD_SERVER_CONTROL_AIR = (byte) 0x83;
    private static final byte CMD_SERVER_QUERY_MILE = (byte) 0x01;
    private static final byte CMD_SERVER_QUERY_OTA_FINISH = (byte) 0x02;
    private static final byte CMD_SERVER_QUERY_OTA_VERSION = (byte) 0x03;

    private static final byte SERVER_RESULT00 = 0x00;
    private static final byte SERVER_RESULT01 = 0x01;
    private static final byte SERVER_RESULT02 = 0x02;

    /**
     * 鉴权指令
     * @param context
     * @return
     */
    public static byte[] getEncryptAuthCmd(Context context) {
        AwLog.d(TAG, "*** 鉴权 ***");
        String index = AwAesUtil.deFile(AwBleSpUtil.getBleMacIndex(context));
        AwLog.d(BleSdkTag, "鉴权用户类型: " + index);
        String userId = AwBleSpUtil.getUserID(context);
        AwLog.d(BleSdkTag, "鉴权用户ID: " + userId);
        //输出明文数组
        byte[] userIdByte = AwByteUtil.hexStringToBytes(userId);
        byte[] indexByte = AwByteUtil.hexStringToBytes(index);
//        byte[] indexByte = {0x00};
        byte[] resultByte = AwByteUtil.mergeBytes(userIdByte, indexByte);
        AwLog.d(TAG, "输出明文数组: " + AwByteUtil.printBytes(resultByte));
        return assembleCmdDataNew(context, (byte) 0x01, resultByte);
    }

    /**
     * 心跳指令
     * @return
     */
    public static byte[] getHeartCmd() {
        AwLog.d(TAG, "*** 心跳 ***");
        return new byte[]{0x09};
    }

    /**
     * 手机型号信息
     * @param context
     * @return
     */
    public static byte[] getModelInfoCmd(Context context) {
        AwLog.d(TAG, "*** 手机型号信息 ***");
        AwLog.d(TAG, "手机型号: " + AwSystemUtil.getSystemModel());
        StringBuffer sb = new StringBuffer();
        sb.append("android").append("\r\n").append(AwSystemUtil.getSystemModel()).append("\r\n");
//        sb.append("ios").append("\r\n").append("iPhone 6s Plus").append("\r\n");
        byte[] dataByte = sb.toString().getBytes();
        //输出明文数组
        AwLog.d(TAG, "输出手机型号信息明文数组: " + AwByteUtil.printBytes(dataByte));
        return assembleCmdDataNew(context, (byte) 0x08, dataByte);
    }

    /**
     * 开闭锁指令
     * @param context
     * @return
     */
    public static byte[] getEncryptLockCmd(Context context, boolean isLock) {
        byte[] dataByte = new byte[1];
        if(isLock) {
            AwLog.d(TAG, "*** 闭锁 ***");
            dataByte[0] = 0x01;
        } else {
            AwLog.d(TAG, "*** 开锁 ***");
            dataByte[0] = 0x00;
        }
        //输出明文数组
        AwLog.d(TAG, "输出明文数组: " + AwByteUtil.printBytes(dataByte));
        return assembleCmdDataNew(context, (byte) 0x01, dataByte);
    }


    /**
     * 寻车指令
     * @param context
     * @return
     */
    public static byte[] getEncryptFindCmd(Context context) {
        AwLog.d(TAG, "*** 寻车 ***");
        byte[] dataByte = {0x01, 0x1E};
        //输出明文数组
        AwLog.d(TAG, "输出明文数组: " + AwByteUtil.printBytes(dataByte));
        return assembleCmdDataNew(context, (byte) 0x02, dataByte);
    }

    public static int CRC16_XMODEM(byte[] buffer) {
        int wCRCin = 0x0000;
        int wCPoly = 0xA8B3;
        for (byte b : buffer) {
            for (int i = 0; i < 8; i++) {
                boolean bit = ((b >> (7 - i) & 1) == 1);
                boolean c15 = ((wCRCin >> 15 & 1) == 1);
                wCRCin <<= 1;
                if (c15 ^ bit)
                    wCRCin ^= wCPoly;
            }
        }
        wCRCin &= 0xffff;
        return wCRCin ^= 0x0000;
    }

    /**
     * 数据组包
     * FLAG + LENGTH + CMD + CRC + DATA
     * @param cmd
     * @param data userId + index位
     * @return
     */
    public static byte[] assembleCmdDataNew(Context context, byte cmd, byte[] data) {
        //转换byte数组
        byte[] secByte = AwByteUtil.hexStringToBytes(AwAesUtil.deFile(AwBleSpUtil.getCurrentSecKey(context)));
        //输出密钥数组
        AwLog.d(BleSdkTag, "输出密钥数组: " + AwByteUtil.printBytes(secByte));
        //STEP1 拼接明文指令
        AwLog.d(BleSdkTag, "明文flag: 00");
        int totalLength = 1 + 1 + 1 + 1 + data.length; //总长度 flag + len + cmd + crc + data
        //cmd + crc + data
        int length = data.length + 2;//CMD 及后面全部数据
        AwLog.d(BleSdkTag, "明文len: " + Integer.toHexString(length));
        byte[] tempData = new byte[length];
        tempData[0] = cmd;
        AwLog.d(BleSdkTag, "明文cmd：" + AwByteUtil.printBytes(new byte[]{tempData[0]}));
        tempData[1] = 0x00;
        System.arraycopy(data, 0, tempData, 2, data.length);
        AwLog.d(BleSdkTag, "明文data：" + AwByteUtil.printBytes(data));
        AwLog.d(BleSdkTag, "明文cmd + crc + data：" + AwByteUtil.printBytes(tempData));
        byte len = AwByteUtil.hexStringToBytes(Integer.toHexString(tempData.length))[0];
        AwLog.d(BleSdkTag, "明文data len：" + AwByteUtil.printBytes(new byte[]{len}));
        byte[] allData = new byte[totalLength];
        allData[0] = 0x00;
        allData[1] = len;
        System.arraycopy(tempData, 0, allData, 2, tempData.length);
        AwLog.d(BleSdkTag, "明文flag + len + cmd + crc + data：" + AwByteUtil.printBytes(allData));

        //STEP2 计算整体明文CRC，并替换
        byte crc1 = (byte) CRC16_XMODEM(allData);
        AwLog.d(BleSdkTag, "明文CRC1：" + AwByteUtil.printBytes(new byte[]{crc1}));
        allData[3] = crc1;
        tempData[1] = crc1;
        AwLog.d(BleSdkTag,  "CRC后, 明文flag + len + cmd + crc + data：" + AwByteUtil.printBytes(allData));

        //STEP3 对第2步最终明文指令中的cmd + crc + param区域进行AES加密
        AwLog.d(BleSdkTag,  "明文待加密 cmd + crc + data：" + AwByteUtil.printBytes(tempData));
        byte[] encryptCmd = AwAesUtil.encrypt(tempData, secByte);
        //输出加密结果数组
        AwLog.d(BleSdkTag, "输出加密结果数组 print: " + AwByteUtil.printBytes(encryptCmd));

        //STEP4 替换加密后的数据长度, 重新拼接新字节数组 flag + len + 加密后的字节数组
        int encryptLength = encryptCmd.length;
        byte encryptLen = AwByteUtil.hexStringToBytes(Integer.toHexString(encryptLength))[0];
        AwLog.d(BleSdkTag,  "加密后 len：" + AwByteUtil.printBytes(new byte[]{encryptLen}));
        byte[] resultByte = new byte[1 + 1 + encryptLength];
        resultByte[0] = 0x00;
        resultByte[1] = encryptLen;
        System.arraycopy(encryptCmd, 0, resultByte, 2, encryptCmd.length);
        //输出加密后重新组装结果数组(替换len)
        AwLog.d(BleSdkTag, "输出加密后重新组装结果数组(替换len, 重新组装) print: " + AwByteUtil.printBytes(resultByte));

        //STEP5 替换flag，flag0-6位长度差 bit7代表是否加密
        int lenDiff = encryptLength - length;
        AwLog.d(BleSdkTag,  "长度差(密文Flag int)：" + lenDiff + " ,encryptLength: " + encryptLength + " ,length: " + length);
        String s = AwByteUtil.add0ToBinaryStart(lenDiff, 8);
        AwLog.d(BleSdkTag, "长度差toBinaryString：" + s);
        //替换首位为1(加密tag)
        s = "1" + s.substring(1);
        AwLog.d(BleSdkTag, "高位替换1：" + s);
        String s16 = AwByteUtil.from2To16(s);
        AwLog.d(BleSdkTag, "转换为16进制字符串：" + s16);
        byte lenDiffLen = AwByteUtil.hexStringToBytes(s16)[0];
        AwLog.d(BleSdkTag, "长度差(密文Flag byte)：" + AwByteUtil.printBytes(new byte[]{lenDiffLen}));
        //替换原有flag.
        resultByte[0] = lenDiffLen;
        //输出加密后重新组装结果数组(替换len)
        AwLog.d(BleSdkTag, "输出加密并替换FLAG重新组装结果数组 print: " + AwByteUtil.printBytes(resultByte));
        return resultByte;
    }

    public static byte[] decryptCmdData(Context context, byte[] data) {
        //转换byte数组
        byte[] secByte = AwByteUtil.hexStringToBytes(AwAesUtil.deFile(AwBleSpUtil.getCurrentSecKey(context)));
        //输出密钥数组
        AwLog.d(BleSdkTag, "输出密钥数组: " + AwByteUtil.printBytes(secByte));
        //STEP1 收到数据包，第一位flag，第二位len_encryptData,截取3～len_encryptData长度第数据为encryptData
        byte[] encryptData = new byte[data.length - 2];
        System.arraycopy(data, 2, encryptData, 0, data.length - 2);
        //输出加密数据(截取第三位开始, cmd + crc + 原始data)
        AwLog.d(BleSdkTag, "输出蓝牙加密数据(截取第三位开始, cmd + crc + 原始data) print: " + AwByteUtil.printBytes(encryptData));

        //STEP2 对encryptData解密获取decryptData
        byte[] originByte = AwAesUtil.decrypt(encryptData, secByte);
        if(originByte == null) {
            AwLog.d(BleSdkTag, "输出蓝牙解密出错，密钥或参数不符");
            return null;
        }
        //输出解密数据(截取第三位开始, cmd + crc + 原始data)
        AwLog.d(BleSdkTag, "输出蓝牙解密数据 print: " + AwByteUtil.printBytes(originByte));

        //STEP3 校验crc
        byte originByteLen = AwByteUtil.hexStringToBytes(Integer.toHexString(originByte.length))[0];
        //所有数据长度, 从flag开始
        byte[] newBytes = new byte[originByte.length + 1 + 1];
        originByte[1] = 0x00;
        newBytes[0] = 0x00;
        newBytes[1] = originByteLen;
        System.arraycopy(originByte, 0, newBytes, 2, originByte.length);
        //输出解密后重新拼接的数组
        AwLog.d(BleSdkTag, "输出解密后重新拼接的数组 print: " + AwByteUtil.printBytes(newBytes));
        byte crc = (byte) CRC16_XMODEM(newBytes);
        AwLog.d(TAG, "明文CRC：" + AwByteUtil.printBytes(new byte[]{crc}));

        //STEP4 校验crc通过，decryptData第一位cmd，第二位crc，剩余为data
        byte[] dataByte = new byte[originByte.length - 1 - 1];
        System.arraycopy(originByte, 2, dataByte, 0, originByte.length - 2);
        AwLog.d(BleSdkTag, "输出解密后CRC校验通过数据 print: " + AwByteUtil.printBytes(dataByte));
        //本地暂不校验CRC，因异步通知，无硬件可调试， 不确认蓝牙数据通讯是否有穿插
        return originByte;
//        return dataByte;//理论返回这条元数据
    }

    public static byte[] decryptCmdDataNoCRC(Context context, byte[] data) {
        //转换byte数组
        byte[] secByte = AwByteUtil.hexStringToBytes(AwAesUtil.deFile(AwBleSpUtil.getCurrentSecKey(context)));
        //输出密钥数组
        AwLog.d(BleSdkTag, "输出密钥数组: " + AwByteUtil.printBytes(secByte));
        //STEP1 收到数据包，第一位flag，第二位len_encryptData,截取3～len_encryptData长度第数据为encryptData
        byte[] encryptData = new byte[data.length - 2];
        System.arraycopy(data, 2, encryptData, 0, data.length - 2);
        //输出加密数据(截取第三位开始, cmd + crc + 原始data)
        AwLog.d(BleSdkTag, "输出蓝牙加密数据(截取第三位开始, cmd + crc + 原始data) print: " + AwByteUtil.printBytes(encryptData));

        //STEP2 对encryptData解密获取decryptData
        byte[] originByte = AwAesUtil.decrypt(encryptData, secByte);
        if(originByte == null) {
            AwLog.d(BleSdkTag, "输出蓝牙解密出错，密钥或参数不符");
            return null;
        }
        //输出解密数据(截取第三位开始, cmd + crc + 原始data)
        AwLog.d(BleSdkTag, "输出蓝牙解密数据 print: " + AwByteUtil.printBytes(originByte));
        return originByte;
    }

    public static void judgeBleCmdResult(Context context, int type, BluetoothGattCharacteristic characteristic, OnCmdListener cmdListener) {
        if (cmdListener == null) {
            AwLog.d(BleSdkTag, "ble control judgeCommunicationResult, however, cmdListener is null");
            return;
        }
        byte[] bleCallbackBytes = characteristic.getValue();
        byte[] resultByte = decryptCmdDataNoCRC(context, bleCallbackBytes);
        AwLog.d(BleSdkTag, "cmd callback, byte: " + AwByteUtil.bytesToHexString(resultByte) + " ,length: " + resultByte.length);
        byte index0Byte = resultByte[0]; //CMD
        byte index1Byte = resultByte[1]; //CRC
        byte[] dataBytes = new byte[resultByte.length - 2]; //其他Data
        System.arraycopy(resultByte, 2, dataBytes, 0, resultByte.length - 2);
        AwLog.d(BleSdkTag, "UUID: " + characteristic.getUuid().toString());
        AwLog.d(BleSdkTag, "输出解密后cmd: " + AwByteUtil.printBytes(new byte[]{index0Byte}));
        AwLog.d(BleSdkTag, "输出解密后CRC: " + AwByteUtil.printBytes(new byte[]{index1Byte}));
        AwLog.d(BleSdkTag, "输出解密后data: " + AwByteUtil.printBytes(dataBytes));
        if(characteristic.getUuid().toString().contains("7800")) {
            //鉴权
            if(index0Byte == CMD_SERVER_AUTH) {
                AwLog.d(BleSdkTag, "cmd auth from ble result: " + AwByteUtil.bytesToHexString(resultByte));
                if(dataBytes[0] == SERVER_RESULT00) {
                    cmdListener.onAuthSuccess();
                } else if(dataBytes[0] == SERVER_RESULT01) {
//                    cmdListener.onAuthSuccess();
                    cmdListener.onAuthFail();
                } else if(dataBytes[0] == SERVER_RESULT02) {
                    cmdListener.onAuthFail();
                }
            }
        } else if(characteristic.getUuid().toString().contains("7801")) {
            int controlType = -1;
            //控车
            if(index0Byte == CMD_SERVER_CONTROL_LOCK) {
                AwLog.d(BleSdkTag, "cmd control unlock from ble result: " + AwByteUtil.bytesToHexString(resultByte));
                if(type != CMD_TYPE_DEFAULT) {
                    if(type == CMD_TYPE_UNLOCK) {
                        controlType = BleConfig.CMD_TYPE_UNLOCK;
                        if(dataBytes[0] == SERVER_RESULT00) {
                            cmdListener.onControlSuccess(type);
                        } else if(dataBytes[0] == SERVER_RESULT01) {
                            cmdListener.onControlFail(controlType, "解锁失败");
                        }
                    } else if(type == CMD_TYPE_LOCK) {
                        controlType = BleConfig.CMD_TYPE_LOCK;
                        if(dataBytes[0] == SERVER_RESULT00) {
                            cmdListener.onControlSuccess(type);
                        } else if(dataBytes[0] == SERVER_RESULT01) {
                            cmdListener.onControlFail(controlType, "闭锁失败");
                        }
                    }
                }
            } else if(index0Byte == CMD_SERVER_CONTROL_FIND) {
                controlType = BleConfig.CMD_TYPE_FIND_CAR;
                AwLog.d(BleSdkTag, "cmd control find from ble result: " + AwByteUtil.bytesToHexString(resultByte));
                if(dataBytes[0] == SERVER_RESULT00) {
                    cmdListener.onControlSuccess(controlType);
                } else if(dataBytes[0] == SERVER_RESULT01) {
                    cmdListener.onControlFail(controlType, "寻车失败");
                }
            } else if(index0Byte == CMD_SERVER_CONTROL_AIR) {
                //文档不符需求. 暂不添加逻辑
            }
        } else if(characteristic.getUuid().toString().contains("7802")) {
            //查询

        } else if(characteristic.getUuid().toString().contains("7803")) {
            //上报
            if(index0Byte == CMD_SERVER_QUERY_MILE) {
                CarStatusBean carStatusBean = new CarStatusBean();
                AwLog.d(BleSdkTag, "cmd query miles from ble result: " + AwByteUtil.bytesToHexString(resultByte));
                if(dataBytes.length > 3) {
                    byte[] mileBytes = new byte[2]; //里程
                    mileBytes[0] = dataBytes[0];
                    mileBytes[1] = dataBytes[1];
                    carStatusBean.setMiles(String.valueOf(AwByteUtil.hexToDec(AwByteUtil.bytesToHexString(mileBytes))));
                    carStatusBean.setDoorStatus(dataBytes[2] == 0x00 ? VehicleStatusEnum.DOOR_STATUS_CLOSE.code : VehicleStatusEnum.DOOR_STATUS_OPEN.code); //门锁状态
                    carStatusBean.setSoc(String.valueOf(AwByteUtil.hexToDec(AwByteUtil.byteToHexString(dataBytes[3]))));
                    AwLog.d(BleSdkTag, "ble vehicle status..." + carStatusBean.toString());
                    cmdListener.onCarStatus(carStatusBean);
                }
            }
        } else if(characteristic.getUuid().toString().contains("7804")) {
            //配对

        }
    }
}
